﻿using System;
using System.Collections.Generic;
using Romulus.Nes;
using Romulus.Nes.Mappers;

namespace SolKeyTest.Data
{
    /// <summary>
    /// Represents a Solomon's Key rom
    /// </summary>
    public class SolRom:Rom<CxRomController> // This game uses the CNROM mapper.
    {
        public static int MAX_LEVEL = 53;
        public static int MIN_LEVEL = 1;

        // Location of background tiles
        static pHRom bgPatternsOffset = (pHRom)0x9010;
        // Location of sprite tiles
        static pHRom spritePatternsOffset = (pHRom)0x8010;
        static int patternTableLength = 0x2000;
        // Palette location
        static pHRom paletteOffset = (pHRom)0x161a;

        // Pointer to level 1 enemy list is at val($5d31)val(5cfc).
        // Similar construct for items.
        // See documentation.
        static readonly pHRom enemyPointerLowBytesOffset = (pHRom)0x5cfc;
        static readonly pHRom enemyPointerHighBytesOffset = (pHRom)0x5d31;

        static readonly pHRom itemPointerLowBytesOffset = (pHRom)0x6a2c;
        static readonly pHRom itemPointerHighBytesOffset = (pHRom)0x6a61;

        static readonly pHRom levelBlockDataOffset = (pHRom)0x603C;
        static readonly int levelBlockDataLength = 24;

        public List<Level> Levels = new List<Level>();

        // Variable to load tiles to
        public List<PatternTable> bgPatterns { get; private set; }
        //public PatternTable bgPatterns { get; private set; }
        public PatternTable spritePatterns { get; private set; }
        public pHRom PaletteOffset { get { return paletteOffset; } }

        public pHRom EnemyPointerLowOffset { get { return enemyPointerLowBytesOffset; } }
        public pHRom EnemyPointerHighOffset { get { return enemyPointerHighBytesOffset; } }
        public pHRom ItemPointerLowOffset { get { return itemPointerLowBytesOffset; } }
        public pHRom ItemPointerHighOffset { get { return itemPointerHighBytesOffset; } }
        public pHRom LevelBlockOffset { get { return levelBlockDataOffset; } }
        public int LevelBlockLength { get { return levelBlockDataLength; } }

        // This Mapper object can be used to access data with pointers
        // or to convert beteen ROM offsets and NES addresses (a.k.a. pointers).
        public Mapper Mapper { get; private set; }

        public SolRom(string file)
            :base(file) {

            // Load our tiles
            // There are four different sets of bg patterns we'll use in different levels.
            // We only need to use three because the last one is used only for the title screen.
            bgPatterns = new List<PatternTable>();
            bgPatterns.Add(new PatternTable(true));
            bgPatterns[0].LoadWholeTable(data, bgPatternsOffset);

            bgPatterns.Add(new PatternTable(true));
            bgPatterns[1].LoadWholeTable(data, bgPatternsOffset + patternTableLength);

            bgPatterns.Add(new PatternTable(true));
            bgPatterns[2].LoadWholeTable(data, bgPatternsOffset + patternTableLength * 2);

            spritePatterns = new PatternTable(true);
            spritePatterns.LoadWholeTable(data, spritePatternsOffset);

            Random rnd = new Random();
            for (int i = 0; i < MAX_LEVEL; i++)
            {
                Levels.Add(new Level(i + 1, this, 0));
            }

            // CNROM doesn't support PRG bank switching, so there is
            // only one Mapper object, which we get with GetMapper.
            //
            // Other mappers, such as MMC1, have bank switching. In
            // the case of MMC1, different banks can be accessed
            // by indexing Mappers, i.e. Mappers[0], Mappers[1], etc..
            this.Mapper = this.Mappers.GetMapper();

            
            // Example of mapper usage:
            
            // (Pointers needs to be in ROM address space, $8000-$FFFF.
            // this means you need to verify your pointers.)
            pCpu pointer = new pCpu(0x8765); 
            
            //  romFileOffset will have the location within the
            //  .NES file of the data 'pointer' points to.
            pHRom romFileOffset = Mapper.ToOffset(pointer);

            // We can also read objects from the ROM via pointer with the mapper
            var enemyPosition = Mapper.GetObject<sampleDataStructure>(pointer);
            // This way we don't have to manually de-reference pointers.
        }

        /// <summary>
        /// This is an example of a data structure you might find in a ROM,
        /// where two bytes specify a screen coordinate.
        /// </summary>
        struct sampleDataStructure
        {
            //public byte x, y;
        }

        public Level GetLevel(int Level)
        {
            return Levels[Level-1];
        }
    }
}
